package me.shiqiang.im.service;

import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import me.shiqiang.im.dao.ServerNodeDao;
import me.shiqiang.im.dao.UserGroupsDao;
import me.shiqiang.im.domain.GroupProperties;
import me.shiqiang.im.domain.ServerNode;
import me.shiqiang.im.domain.UserGroups;
import me.shiqiang.im.redis.RedisKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

/**
 * @author cuishiqiang
 * @date 2017/12/15 下午6:49
 */
@Service
public class ServerNodeService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    ServerNodeDao serverNodeDao;

    @Autowired
    UserGroupsDao userGroupsDao;

    @Value("${com.tst.rocketmq.producer.topic}")
    private String topic;

    @Autowired
    @Qualifier("event")
    Producer producer;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    UserService userService;

    public void save(ServerNode serverNode) {
        serverNodeDao.save(serverNode);
    }

    public String getServerAddress(String userId) {
        if (!userService.useAuth(Long.valueOf(userId))) {
            return "";
        }
        String serverId = stringRedisTemplate.opsForValue().get(RedisKey.userServerId(userId));
        if (serverId == null) {
            Set<Integer> nodeSet = new HashSet<>();
            if (!allocateServer(userId, nodeSet)) {
                return "";
            }else {
                serverId = stringRedisTemplate.opsForValue().get(RedisKey.userServerId(userId));
                UserGroups userGroups = userGroupsDao.findOne(Long.valueOf(userId));
                if (userGroups == null) {
                    return "";
                }
                for (GroupProperties groupProperties : userGroups.getGroups().values()) {
                    sendMqMsg(groupProperties.getGroupId().toString());
                }
            }
        }
        ServerNode one = serverNodeDao.findOne(serverId);
        return one.getAddress();
    }

    public String getServerId(String userId) {
        return stringRedisTemplate.opsForValue().get(RedisKey.userServerId(userId));
    }

    public boolean allocateServer(String userId, Set<Integer> nodeSet) {
        int serverCount = (int) serverNodeDao.count();
        int randomServerId = new Random().nextInt(serverCount) + 1;
        if (nodeSet.contains(randomServerId)) {
            allocateServer(userId, nodeSet);
        }
        ServerNode serverNode = serverNodeDao.findOne(String.valueOf(randomServerId));
        String serverId = serverNode.getServerId();
        logger.info("userId:{},serverid:{},serverCount:{},randomServerId:{}",userId,serverId,serverCount,randomServerId );
        Long usedCount = stringRedisTemplate.opsForValue().increment(serverId, 1);
        if (usedCount < serverNode.getTotal()) {
            stringRedisTemplate.opsForValue().set(RedisKey.userServerId(userId), serverNode.getServerId());
            stringRedisTemplate.opsForSet().add(RedisKey.serverUsersKey(serverNode.getServerId()), userId);
        }else {
            nodeSet.add(randomServerId);
            if (nodeSet.size() >= serverCount) {
                return false;
            }
            allocateServer(userId, nodeSet);
        }
        return true;
    }

    public void setUserServerIp(String userId, String serverIp) {
        stringRedisTemplate.opsForValue().set(RedisKey.userServerId(userId), serverIp);
    }

    public Collection<String> getNeedNotifyUsers(String serverId, Long groupId) {
        return stringRedisTemplate.opsForSet().intersect(RedisKey.serverUsersKey(serverId), RedisKey.groupUserSet(groupId));
    }

    private void sendMqMsg(String body) {
        Message msg = new Message();
        msg.setTopic(topic);
        msg.setTag("group_change");
        msg.setBody(body.getBytes());
        producer.sendOneway(msg);
    }

    public void addToGroup(String groupId, String serverId) {
        stringRedisTemplate.opsForSet().add(groupId, serverId);
    }

    public Set<String> getGroupServerIdSet(String groupId) {
        return stringRedisTemplate.opsForSet().members(groupId);
    }

    public void deleteFromGroup(String groupId, String serverId) {
        stringRedisTemplate.opsForSet().remove(groupId, serverId);
    }
}
